package cn.legym.garp.gateway.traffic.rule;

import cn.legym.garp.gateway.traffic.config.TrafficDataSourceListener;
import com.google.common.collect.*;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

/**
 * create by pipisun on 2021/9/7
 * copyright © 2017-2021 Legym Technology Co.,Ltd. All rights reserve
 * d.
 * file description:
 * last update by {} on {}
 * update description:
 */
@Component
public final class TrafficRuleManager implements SmartInitializingSingleton, TrafficDataSourceListener<TrafficRule> {

    // TODO multimap
//    private static final Multimap<String, TrafficRule> RULES = Multimaps.newMultimap(Maps.newConcurrentMap(), Sets::newConcurrentHashSet);
    private static final Map<String, TrafficRule> RULES = Maps.newConcurrentMap();

    public Map<String, TrafficRule> getRules() {
        return RULES;
    }

    public Set<TrafficRule> matchRule(ServerHttpRequest request) {
        Set<TrafficRule> ret = Sets.newHashSet();
        for (Map.Entry<String, TrafficRule> entry : RULES.entrySet()) {
            if (entry.getValue().matchRule(request)) {
                ret.add(entry.getValue());
            }
        }
        return ret;
    }

    @Override
    public void afterSingletonsInstantiated() {

    }

    @Override
    public void updateData(Collection<TrafficRule> source) {
        if (source == null) {
            return;
        }
        Map<String, TrafficRule> map = source.stream()
                .peek(TrafficRule::init)
                .collect(Collectors.toMap(TrafficRule::getUrlPattern, r -> r,
                        BinaryOperator.minBy(Comparator.comparingInt(TrafficRule::hashCode))));
        RULES.putAll(map);
        RULES.keySet().stream().filter(k -> !map.containsKey(k)).forEach(RULES::remove);
    }
}
